package com.dreamliner.lib.lame;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Message;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author chenzj
 * @Title: MP3Recorder
 * @Description: 类的描述 -
 * @date 2017/6/30 23:08
 * @email admin@chenzhongjin.cn
 */
public class MP3Recorder {

    // =======================AudioRecord Default
    // Settings=======================
    private static final int DEFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
    /**
     * 以下三项为默认配置参数。Google Android文档明确表明只有以下3个参数是可以在所有设备上保证支持的。
     */
    private static final int DEFAULT_SAMPLING_RATE = 44100;// 模拟器仅支持从麦克风输入8kHz采样率
    private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
    /**
     * 下面是对此的封装 private static final int DEFAULT_AUDIO_FORMAT =
     * AudioFormat.ENCODING_PCM_16BIT;
     */
    private static final PCMFormat DEFAULT_AUDIO_FORMAT = PCMFormat.PCM_16BIT;

    // ======================Lame Default Settings=====================
    private static final int DEFAULT_LAME_MP3_QUALITY = 7;
    /**
     * 与DEFAULT_CHANNEL_CONFIG相关，因为是mono单声，所以是1
     */
    private static final int DEFAULT_LAME_IN_CHANNEL = 1;
    /**
     * Encoded bit rate. MP3 file will be encoded with bit rate 32kbps
     */
    private static final int DEFAULT_LAME_MP3_BIT_RATE = 32;

    // ==================================================================

    /**
     * 自定义 每160帧作为一个周期，通知一下需要进行编码
     */
    private static final int FRAME_COUNT = 160;
    private AudioRecord mAudioRecord = null;
    private int mBufferSize;
    private short[] mPCMBuffer;
    private DataEncodeThread mEncodeThread;
    private volatile boolean mIsRecording = false;
    private File mRecordFile;

    private long mStartTime;
    /**
     * Default constructor. Setup recorder with default sampling rate 1 channel,
     * 16 bits pcm
     *
     * @param recordFile
     * target file
     */

    // Thread pool
    private ExecutorService mThreadPool;

    // 创建private static类实例
    private volatile static MP3Recorder INSTANCE;
    // 同步锁
    private static Object INSTANCE_LOCK = new Object();

    /**
     * 使用单例模式创建--双重锁定
     */
    public static MP3Recorder getInstance(Context context) {
        if (INSTANCE == null)
            synchronized (INSTANCE_LOCK) {
                if (INSTANCE == null) {
                    INSTANCE = new MP3Recorder();
                }
            }
        return INSTANCE;
    }

    public MP3Recorder() {
        mThreadPool = Executors.newCachedThreadPool();
    }

    public MP3Recorder(File recordFile) {
        mRecordFile = recordFile;
    }

    public void setRecordFile(File mRecordFile) {
        this.mRecordFile = mRecordFile;
    }

    /**
     * Start recording. Create an encoding thread. Start record from this
     * thread.
     *
     * @throws IOException initAudioRecorder throws
     */
    public void start() throws IOException {
        if (mIsRecording)
            return;
        initAudioRecorder();
        mAudioRecord.startRecording();
        //起始时间
        this.mStartTime = System.currentTimeMillis();
        mIsRecording = true;
        // 计算录音长度和音量的变化
        mThreadPool.execute(new RecordingChangeUpdater());
        new Thread() {
            @Override
            public void run() {
                // 设置线程权限
                android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                mIsRecording = true;
                try {
                    while (mIsRecording) {
                        int readSize = mAudioRecord.read(mPCMBuffer, 0, mBufferSize);
                        if (readSize > 0) {
                            mEncodeThread.addTask(mPCMBuffer, readSize);
                            calculateRealVolume(mPCMBuffer, readSize);
                        }
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                    mIsRecording = false;
                    cancelRecording();
                }
            }

            /**
             * 此计算方法来自samsung开发范例
             *
             * @param buffer
             *            buffer
             * @param readSize
             *            readSize
             */
            private void calculateRealVolume(short[] buffer, int readSize) {
                double sum = 0;
                for (int i = 0; i < readSize; i++) {
                    // 这里没有做运算的优化，为了更加清晰的展示代码
                    sum += buffer[i] * buffer[i];
                }
                if (readSize > 0) {
                    double amplitude = sum / readSize;
                    mVolume = (int) Math.sqrt(amplitude);
                }
            }
        }.start();
    }

    private int mVolume;

    public void stop() {
        mIsRecording = false;
    }

    public boolean isRecording() {
        return mIsRecording;
    }

    /**
     * Initialize audio recorder
     */
    private void initAudioRecorder() throws IOException {
        mBufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG,
                DEFAULT_AUDIO_FORMAT.getAudioFormat());

        int bytesPerFrame = DEFAULT_AUDIO_FORMAT.getBytesPerFrame();
        /*
         * Get number of samples. Calculate the buffer size (round up to the
		 * factor of given frame size) 使能被整除，方便下面的周期性通知
		 */
        int frameSize = mBufferSize / bytesPerFrame;
        if (frameSize % FRAME_COUNT != 0) {
            frameSize += (FRAME_COUNT - frameSize % FRAME_COUNT);
            mBufferSize = frameSize * bytesPerFrame;
        }

		/* Setup audio recorder */
        mAudioRecord = new AudioRecord(DEFAULT_AUDIO_SOURCE, DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG,
                DEFAULT_AUDIO_FORMAT.getAudioFormat(), mBufferSize);

        mPCMBuffer = new short[mBufferSize];
        /*
         * Initialize lame buffer mp3 sampling rate is the same as the recorded
		 * pcm sampling rate The bit rate is 32kbps
		 */
        LameUtil.init(DEFAULT_SAMPLING_RATE, DEFAULT_LAME_IN_CHANNEL, DEFAULT_SAMPLING_RATE, DEFAULT_LAME_MP3_BIT_RATE,
                DEFAULT_LAME_MP3_QUALITY);
        // Create and run thread used to encode data
        // The thread will
        mEncodeThread = new DataEncodeThread(mRecordFile, mBufferSize);
        mEncodeThread.start();
        mAudioRecord.setRecordPositionUpdateListener(mEncodeThread, mEncodeThread.getHandler());
        mAudioRecord.setPositionNotificationPeriod(FRAME_COUNT);
    }

    private Mp3RecordListener mChangListener;

    private static int VOLUME_STANDARD_VAL = 2000;

    public static void setVolumeStandVal(int volumeStandVal) {
        VOLUME_STANDARD_VAL = volumeStandVal;
    }

    public void setOnRecordChangeListener(Mp3RecordListener listener) {
        mChangListener = listener;
    }

    private static final int UPDATE_VOICE_CHANGE = 10;// /更新语音图片

    private final Handler mHandler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == UPDATE_VOICE_CHANGE) {
                int volume = msg.arg1;
                int time = msg.arg2;
                if (null != mChangListener) {
                    int volumePercentVal = volume / VOLUME_STANDARD_VAL;
                    mChangListener.onVolumeChanged(volumePercentVal > 4 ? 4 : volumePercentVal);
                    if (time % 10 == 0) {
                        mChangListener.onTimeChanged((time / 10), mRecordFile.getAbsolutePath());
                    }
                }
                return true;
            } else {
                return false;
            }

        }
    });

    private final class RecordingChangeUpdater implements Runnable {

        @Override
        public void run() {
            int currentRecordCounter = 0;
            while (isRecording()) {
                // 更新图片
                Message msg = new Message();
                msg.arg1 = mVolume;// 当前音量大小
                msg.arg2 = currentRecordCounter;// 当前录音时长
                msg.what = UPDATE_VOICE_CHANGE;
                mHandler.sendMessage(msg);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
                currentRecordCounter++;
            }
        }
    }

    public void startRecording(String path) {

        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        // 在当前用户的目录下面存放录音文件
        File audioFile = new File(path + File.separator + System.currentTimeMillis() + ".mp3");
        try {
            if (!audioFile.exists()) {
                audioFile.createNewFile();
            }
        } catch (IOException e) {
        }

        setRecordFile(audioFile);
        try {
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void cancelRecording() {
        // release and finalize audioRecord
        if (mAudioRecord != null) {
            // 设置为false--表明录音结束
            mIsRecording = false;

            mAudioRecord.stop();
            mAudioRecord.release();
            mAudioRecord = null;
            // stop the encoding thread and try to wait
            // until the thread finishes its job
            Message msg = Message.obtain(mEncodeThread.getHandler(), DataEncodeThread.PROCESS_STOP);
            msg.sendToTarget();

            // 放弃录音就删除指定待存储的文件
            if ((this.mRecordFile != null) && (this.mRecordFile.exists()) && (!this.mRecordFile.isDirectory()))
                this.mRecordFile.delete();
        }
    }

    public void stopRecording() {

        if (null != mAudioRecord) {
            mIsRecording = false;

            mAudioRecord.stop();
            mAudioRecord.release();
            mAudioRecord = null;

            int length = (int) ((System.currentTimeMillis() - mStartTime) / 1000);

            Message msg = Message.obtain(mEncodeThread.getHandler(), DataEncodeThread.PROCESS_STOP, length, 0, mChangListener);
            msg.sendToTarget();
        }
    }
}
